#include "keepalive_module.h"

#include "WLcJSON.h"
#include "burst_module.h"
#include "wl_http_client.h"
#include "location_module.h"
#include "login_module.h"
#include "protocol_client.h"
#include "repo.h"
#include "session_module.h"
#include "weilapb.h"
#include "weilapb_defines.h"
#include "wl_ext_fs.h"
#include "wl_ext_network.h"
#include "wlsdk_define.h"

#ifdef __cplusplus
extern "C" {
#endif

#define HEARTBEAT_INTERVAL_MAX 300
#define HEARTBEAT_INTERVAL_MIN 60
#define HEARTBEAT_INTERVAL_STEP 30

#define RECONNECT_TIMER_INTERVAL 5   // 重连定时器间隔5s
#define DAEMON_TIMER_INTERVAL 20     // 守护定时器间隔20s
#define BATTERY_REPORT_INTERVAL 300  // 电量上报间隔300s

#define RECONNECT_INTERVAL_OVERTIME 120
#define RECONNECT_INTERVAL_MAX 30
#define RECONNECT_INTERVAL_MIN 15
#define RECONNECT_INTERVAL_STEP 15
#define REFRESH_TOKEN_INTERVAL 3600  // 1小时刷新一次token
#define NETWORK_INVALID_MAX 90       // 网络无效最长时间
#define SYSTEM_INVALID_MAX 300       // 网络无效最长时间
#define SYSTEM_SAVE_INTERVAL 180     // 180秒保持一次系统信息

#define SYSTEM_MEMORY_THRESHOLD_TIMES (3600 / DAEMON_TIMER_INTERVAL)

#define VERSION_CHECK_INTERVAL 24 * 3600  // 24小时检查一次版本
#define OTA_FILE_NAME "ota.bin"

#define SERVER_URL(server_url, host, port, method)                                                                     \
    sprintf(server_url, "http://%s:%d/v1/devices/client/%s", host, port, method)

typedef enum {
    KEEPALIVE_HTTP_REQUEST_CHECK_VERSION      = 0,
    KEEPALIVE_HTTP_REQUEST_ALARM_POWER        = 1,
    KEEPALIVE_HTTP_REQUEST_REPORT_REBOOT      = 2,
    KEEPALIVE_HTTP_REQUEST_REPORT_RESET_MODEM = 3,
    KEEPALIVE_HTTP_REQUEST_DOWNLOAD_OTA_FILE  = 4
} keepalive_http_request_type;

typedef struct {
    wlsdk_message_callback     callback;
    wl_int32                   next_system_save_left;         // 下次系统保持剩余时间
    wl_time                    next_battery_timestamp;        // 下次电量上报时间
    wl_time                    next_refresh_token_timestamp;  // 下次access_token刷新时间
    wl_time                    next_reconnect_timestamp;      // 下次重连时间
    wl_time                    reconnect_interval;            // 重连间隔时间
    wl_time                    latest_heartbeat_timestamp;    // 最新心跳时间
    wl_uint32                  network_invalid_duration;      // 网络无效时间
    wl_uint32                  system_invalid_duration;       // 系统无效时间
    wl_uint32                  latest_heartbeat_recvbytes;    // 最新心跳接受数据量
    wl_uint32                  daemon_duration;               // 工作时长
    wl_int32                   check_version_left;
    wl_int32                   heartbeat_interval_max;
    wl_int32                   heartbeat_interval_min;
    wl_int32                   heartbeat_interval;
    wl_int32                   heartbeat_interval_fixed;  // 固定心跳
    wl_int32                   success_times;
    wl_int32                   timeout_times;
    wl_int32                   network_state;
    wl_uint32                  memory_threshold;
    wl_uint32                  memory_threshold_times;
    wl_int32                   memory_reboot_hour;
    wl_int32                   memory_reboot_min;
    wl_bool                    memory_is_reboot;
    WL_TIMER                   daemon_timer;      // 守护定时器
    WL_TIMER                   reconnect_timer;   // 重连定时器
    wl_int32                   ota_status;        // 0 支持 1 不支持
    wlsdk_new_version          ota_version;       // 版本信息
    keepalive_resource_version resource_version;  // 资源版本信息
    wl_int32 latest_alarm_power_percent;     // 最近一次电量告警百分比 小于等于0不告警 大于0告警
    wl_int32 alarm_power_percent;            // 电量告警百分比 小于等于0不告警 大于0告警
    wl_int32 latest_battery_update_percent;  // 最近一次电量更新百分比
    wl_int32 battery_update_percent;         // 电量更新百分比 小于等于0不更新 大于0更新
} wl_keepalive_global;

static wl_keepalive_global s_keepalive_global;

static void process_set_network(void* data, wl_int32 datalen);
static void keepalive_daemon_timer_callback(WL_TIMER timer, void* context);
static void keepalive_reconnect_timer_callback(WL_TIMER timer, void* context);
static wl_int32
    keepalive_protocol_client_request_callback(PBWeilaMsg* message, void* content, wl_uint32 contentlen, void* context);
static wl_int32 keepalive_message_callback(wl_int32 msgid, void* data, wl_uint32 datalen);
static void     send_heartbeat(void);
static void     check_version(void);
static void     reconnect_check(void);
static void     alarm_power(wl_int32 percent);
static void     battery_update(wl_int32 percent);
static void     report_reboot(wl_int32 memory);
static void     report_reset_modem(void);
static void     battery_refresh(void);

static wl_int32 keepalive_protocol_client_listen_callback(PBWeilaMsg* message, void* context) {
    WLLOGD("message %d", message->message);
    switch (message->message) {
        case WL_LOGIN_HEARTBEAT_MSG: {
            PB_UINT32* serverTime = (PB_UINT32*)message->pContent;
            if (serverTime) {
                repo_set_server_timestamp(*serverTime);
            }
            wl_core_send_message(WL_MODULE_KEEPALIVE, KEEPALIVE_MSG_SEND_HEARTBEAT_RESPONSE, wl_null, 0);
            break;
        }
        default:
            break;
    }
    return 0;
}

static void keepalive_sdk_message_callback(wl_uint32 msgid, wl_int32 resultcode, void* msgdata, wl_uint32 datalen) {
    if (s_keepalive_global.callback) {
        s_keepalive_global.callback(msgid, resultcode, msgdata, datalen);
    }
}

static void keepalive_wl_http_client_callback(wl_uint32 result,
                                           wl_char*  content,
                                           wl_uint32 content_len,
                                           wl_uint32 content_type,
                                           void*     context) {
    // WLLOGD("result %d content %d", result, (wl_int32)context);
    if ((wl_int32)context == KEEPALIVE_HTTP_REQUEST_CHECK_VERSION) {
        if (WLSDK_RESULT_SUCCESS == result) {
            if (content && HTTP_CLIENT_CONTENT_JSON == content_type) {
                WLcJSON* result_obj = WLcJSON_Parse(content);
                if (result_obj) {
                    WLcJSON* errcode_obj = WLcJSON_GetObjectItem(result_obj, "errcode");
                    if (errcode_obj) {
                        WLcJSON* data_obj = WLcJSON_GetObjectItem(result_obj, "data");
                        if (data_obj) {
                            wl_int32 resultcode = (wl_int32)WLcJSON_GetNumberValue(errcode_obj);
                            WLLOGD("result %d", resultcode);
                            if (WLSDK_RESULT_SUCCESS == resultcode) {
                                wlsdk_new_version new_version;
                                WLcJSON*          type_obj  = WLcJSON_GetObjectItem(data_obj, "type");
                                WLcJSON* version_number_obj = WLcJSON_GetObjectItem(data_obj, "version_number");
                                WLcJSON* version_obj        = WLcJSON_GetObjectItem(data_obj, "version");
                                WLcJSON* url_obj            = WLcJSON_GetObjectItem(data_obj, "url");
                                WLcJSON* file_size_obj      = WLcJSON_GetObjectItem(data_obj, "file_size");
                                WLcJSON* desc_obj           = WLcJSON_GetObjectItem(data_obj, "desc");
                                WLcJSON* trace_switch_obj   = WLcJSON_GetObjectItem(data_obj, "trace_switch");
                                WLcJSON* trace_level_obj    = WLcJSON_GetObjectItem(data_obj, "trace_level");
                                memset(&new_version, 0, sizeof(wlsdk_new_version));
                                if (version_number_obj && version_obj && url_obj) {
                                    new_version.version_number = (wl_uint32)WLcJSON_GetNumberValue(version_number_obj);
                                    if (WLcJSON_IsString(version_obj)) {
                                        strncpy(new_version.version,
                                                (wl_char*)WLcJSON_GetStringValue(version_obj),
                                                sizeof(new_version.version) - 1);
                                    }
                                    if (WLcJSON_IsString(url_obj)) {
                                        strncpy(new_version.url,
                                                (wl_char*)WLcJSON_GetStringValue(url_obj),
                                                sizeof(new_version.url) - 1);
                                    }
                                    if (WLcJSON_IsNumber(file_size_obj)) {
                                        new_version.file_size = (wl_uint32)WLcJSON_GetNumberValue(file_size_obj);
                                    }

                                    if (desc_obj) {
                                        if (WLcJSON_IsString(desc_obj)) {
                                            strncpy(new_version.desc,
                                                    (wl_char*)WLcJSON_GetStringValue(desc_obj),
                                                    sizeof(new_version.desc) - 1);
                                        }
                                    }
                                    if (WLcJSON_IsNumber(type_obj)) {
                                        new_version.type = (wl_uint32)WLcJSON_GetNumberValue(type_obj);
                                    } else {
                                        new_version.type = 0;
                                    }
                                    if (trace_switch_obj && trace_level_obj) {
                                        if (WLcJSON_IsNumber(trace_switch_obj) && WLcJSON_IsNumber(trace_level_obj)) {
                                            wl_trace_config((wl_bool)WLcJSON_GetNumberValue(trace_switch_obj),
                                                            (wl_uint32)WLcJSON_GetNumberValue(trace_level_obj));
                                        }
                                    }
                                    // 版本检查保存系统信息
                                    repo_set_system_info(s_keepalive_global.daemon_duration,
                                                         0,
                                                         wl_mem_fresh_size() + wl_mem_free_size(),
                                                         wl_mem_sys_size());
                                    memcpy(&s_keepalive_global.ota_version, &new_version, sizeof(wlsdk_new_version));
                                    // APP版本信息
                                    if (0 == new_version.type) {
                                        repo_hardware hardware;
                                        repo_get_hardware(&hardware);
                                        // 版本检查未下载完成不提示新版本
                                        new_version.version_number = hardware.version_number;
                                        strncpy(new_version.version, hardware.version, sizeof(new_version.version) - 1);
                                        new_version.version[sizeof(new_version.version) - 1] = '\0';
                                        // 新版本
                                        if (s_keepalive_global.ota_version.version_number > hardware.version_number) {
                                            // 本地是否已下载
                                            wl_ssize   fsize   = 0;
                                            wl_ext_fs_handle fhandle = wl_ext_fs_open(OTA_FILE_NAME, WL_EXT_RDONLY);
                                            if (WL_EXT_INVALID_FS != fhandle) {
                                                fsize = wl_ext_fs_size_by_handle(fhandle);
                                                wl_ext_fs_close(fhandle);
                                            }
                                            if (0 == fsize || fsize != s_keepalive_global.ota_version.file_size) {
                                                wl_http_client_get_file_with_path(
                                                    s_keepalive_global.ota_version.url,
                                                    OTA_FILE_NAME,
                                                    HTTP_CLIENT_REQUEST_TIMEOUT_60,
                                                    keepalive_wl_http_client_callback,
                                                    (void*)KEEPALIVE_HTTP_REQUEST_DOWNLOAD_OTA_FILE);
                                            } else {
                                                // WLLOGW("ota file size %d is equal new version file_size",
                                                //        fsize,
                                                //        s_keepalive_global.ota_version.file_size);
                                                // wl_char full_path[MAX_URL_LEN] = "";
                                                // wl_full_path(OTA_FILE_NAME, full_path);
                                                strncpy(s_keepalive_global.ota_version.url,
                                                        OTA_FILE_NAME,
                                                        sizeof(s_keepalive_global.ota_version.url) - 1);
                                                WLLOGW("url %s fsize %d", s_keepalive_global.ota_version.url, fsize);
                                                new_version.version_number =
                                                    s_keepalive_global.ota_version.version_number;
                                                strncpy(new_version.version,
                                                        s_keepalive_global.ota_version.version,
                                                        sizeof(s_keepalive_global.ota_version.version) - 1);
                                                new_version.version[sizeof(new_version.version) - 1] = '\0';
                                                strncpy(new_version.url,
                                                        s_keepalive_global.ota_version.url,
                                                        sizeof(new_version.url) - 1);
                                                new_version.url[sizeof(new_version.url) - 1] = '\0';
                                            }
                                        }
                                        keepalive_sdk_message_callback(WLSDK_MSG_SYSTEM_NEW_VERSION,
                                                                       WLSDK_RESULT_SUCCESS,
                                                                       &new_version,
                                                                       sizeof(wlsdk_new_version));
                                    }
                                    // resource版本
                                    else if (1 == new_version.type) {
                                        // 版本检查未下载完成不提示新版本
                                        new_version.version_number = s_keepalive_global.resource_version.version_number;
                                        strncpy(new_version.version,
                                                s_keepalive_global.resource_version.version,
                                                sizeof(new_version.version) - 1);
                                        new_version.version[sizeof(new_version.version) - 1] = '\0';
                                        // 新版本
                                        if (s_keepalive_global.ota_version.version_number
                                            > s_keepalive_global.resource_version.version_number) {
                                            // 本地是否已下载
                                            wl_ssize   fsize   = 0;
                                            wl_ext_fs_handle fhandle = wl_ext_fs_open(OTA_FILE_NAME, WL_EXT_RDONLY);
                                            if (WL_EXT_INVALID_FS != fhandle) {
                                                fsize = wl_ext_fs_size_by_handle(fhandle);
                                                wl_ext_fs_close(fhandle);
                                            }
                                            if (0 == fsize || fsize != s_keepalive_global.ota_version.file_size) {
                                                wl_http_client_get_file_with_path(
                                                    s_keepalive_global.ota_version.url,
                                                    OTA_FILE_NAME,
                                                    HTTP_CLIENT_REQUEST_TIMEOUT_60,
                                                    keepalive_wl_http_client_callback,
                                                    (void*)KEEPALIVE_HTTP_REQUEST_DOWNLOAD_OTA_FILE);
                                            } else {
                                                // WLLOGW("ota file size %d is equal new version file_size",
                                                //        fsize,
                                                //        s_keepalive_global.ota_version.file_size);
                                                // wl_char full_path[MAX_URL_LEN] = "";
                                                // wl_full_path(OTA_FILE_NAME, full_path);
                                                strncpy(s_keepalive_global.ota_version.url,
                                                        OTA_FILE_NAME,
                                                        sizeof(s_keepalive_global.ota_version.url) - 1);
                                                WLLOGW("url %s fsize %d", s_keepalive_global.ota_version.url, fsize);
                                                new_version.version_number =
                                                    s_keepalive_global.ota_version.version_number;
                                                strncpy(new_version.version,
                                                        s_keepalive_global.ota_version.version,
                                                        sizeof(s_keepalive_global.ota_version.version) - 1);
                                                new_version.version[sizeof(new_version.version) - 1] = '\0';
                                                strncpy(new_version.url,
                                                        s_keepalive_global.ota_version.url,
                                                        sizeof(new_version.url) - 1);
                                                new_version.url[sizeof(new_version.url) - 1] = '\0';
                                            }
                                        }
                                        keepalive_sdk_message_callback(WLSDK_MSG_SYSTEM_NEW_VERSION,
                                                                       WLSDK_RESULT_SUCCESS,
                                                                       &new_version,
                                                                       sizeof(wlsdk_new_version));
                                    } else {
                                        keepalive_sdk_message_callback(
                                            WLSDK_MSG_SYSTEM_NEW_VERSION, WLSDK_RESULT_DATA_INVALID, wl_null, 0);
                                    }
                                }
                            } else {
                                keepalive_sdk_message_callback(
                                    WLSDK_MSG_SYSTEM_NEW_VERSION, WLSDK_RESULT_DATA_INVALID, wl_null, 0);
                            }
                        } else {
                            keepalive_sdk_message_callback(
                                WLSDK_MSG_SYSTEM_NEW_VERSION, WLSDK_RESULT_DATA_INVALID, wl_null, 0);
                        }
                    } else {
                        keepalive_sdk_message_callback(
                            WLSDK_MSG_SYSTEM_NEW_VERSION, WLSDK_RESULT_DATA_INVALID, wl_null, 0);
                    }
                    WLcJSON_Delete(result_obj);
                } else {
                    keepalive_sdk_message_callback(WLSDK_MSG_SYSTEM_NEW_VERSION, WLSDK_RESULT_DATA_INVALID, wl_null, 0);
                }
            }
        } else {
            keepalive_sdk_message_callback(WLSDK_MSG_SYSTEM_NEW_VERSION, WLSDK_RESULT_SERVER_EXCEPTION, wl_null, 0);
        }
    } else if ((wl_int32)context == KEEPALIVE_HTTP_REQUEST_ALARM_POWER) {
        keepalive_sdk_message_callback(WLSDK_MSG_SYSTEM_ALARM_POWER, result, wl_null, 0);
    } else if ((wl_int32)context == KEEPALIVE_HTTP_REQUEST_DOWNLOAD_OTA_FILE) {
        if (WLSDK_RESULT_SUCCESS == result) {
            // WLLOGD("KEEPALIVE_HTTP_REQUEST_DOWNLOAD_OTA_FILE");
            if (wl_null != content) {
                wl_ext_fs_handle fhandle = wl_ext_fs_open(content, WL_EXT_RDONLY);
                // WLLOGD("content", content);
                if (WL_EXT_INVALID_FS != fhandle) {
                    wl_ssize fsize = wl_ext_fs_size_by_handle(fhandle);
                    wl_ext_fs_close(fhandle);
                    
                    // WLLOGD("file_size %d fsize %d", s_keepalive_global.ota_version.file_size, fsize);
                    if (s_keepalive_global.ota_version.file_size == fsize) {
                        // wl_char full_path[MAX_URL_LEN] = "";
                        // wl_full_path(content, full_path);
                        strncpy(s_keepalive_global.ota_version.url,
                                content,
                                sizeof(s_keepalive_global.ota_version.url) - 1);
                        WLLOGW("url %s fsize %d", s_keepalive_global.ota_version.url, fsize);
                        keepalive_sdk_message_callback(WLSDK_MSG_SYSTEM_NEW_VERSION,
                                                       WLSDK_RESULT_SUCCESS,
                                                       &s_keepalive_global.ota_version,
                                                       sizeof(wlsdk_new_version));
                    }
                }
            }
        }
    }
}

static void keepalive_start_reconnect_timer(void) {
    if (WL_INVALID_TIMER != s_keepalive_global.reconnect_timer) {
        wl_timer_stop(s_keepalive_global.reconnect_timer);
        s_keepalive_global.reconnect_timer = WL_INVALID_TIMER;
    }
    s_keepalive_global.reconnect_timer = wl_timer_start(RECONNECT_TIMER_INTERVAL * 1000,
                                                        REPETITIVE,
                                                        keepalive_reconnect_timer_callback,
                                                        wl_null,
                                                        WL_TASK_APP,
                                                        "reconnect");
}

static void keepalive_stop_reconnect_timer(void) {
    if (WL_INVALID_TIMER != s_keepalive_global.reconnect_timer) {
        wl_timer_stop(s_keepalive_global.reconnect_timer);
        s_keepalive_global.reconnect_timer = WL_INVALID_TIMER;
    }
}

wl_int32 keepalive_module_init(wlsdk_message_callback callback) {
    wl_int32  ret = 0;
    wl_module module;

    WLLOGI("keepalive_module_init");
    memset(&module, 0, sizeof(wl_module));
    module.moduleid = WL_MODULE_KEEPALIVE;
    module.taskid   = WL_TASK_APP;
    module.callback = keepalive_message_callback;
    ret             = wl_core_register_module(&module);
    if (0 != ret) {
        WLLOGW("wl_core_register_module keepalive fail");
        return ret;
    }
    memset(&s_keepalive_global, 0, sizeof(wl_keepalive_global));
    s_keepalive_global.memory_reboot_hour     = 4;
    s_keepalive_global.memory_reboot_min      = wl_getElapsedTick() % 60;  // 分钟随机,避免同时重启
    s_keepalive_global.memory_is_reboot       = wl_false;
    s_keepalive_global.memory_threshold       = WL_MEMORY_SYSTEM_THRESHOLD;
    s_keepalive_global.memory_threshold_times = 0;
    s_keepalive_global.callback               = callback;
    s_keepalive_global.daemon_timer           = wl_timer_start(
        DAEMON_TIMER_INTERVAL * 1000, REPETITIVE, keepalive_daemon_timer_callback, wl_null, WL_TASK_APP, "daemon");
    s_keepalive_global.reconnect_interval       = RECONNECT_INTERVAL_MIN;
    s_keepalive_global.next_reconnect_timestamp = repo_get_server_timestamp() + s_keepalive_global.reconnect_interval;
    keepalive_start_reconnect_timer();
    protocol_client_add_listen(
        WL_LOGIN_HEARTBEAT_MSG, WEILA_MSG_REQ, keepalive_protocol_client_listen_callback, wl_null);
    return ret;
}

wl_int32 keepalive_module_end(void) {
    WLLOGI("keepalive_module_end");
    wl_core_unregister_module(WL_MODULE_KEEPALIVE);
    s_keepalive_global.callback = wl_null;
    if (WL_INVALID_TIMER != s_keepalive_global.daemon_timer) {
        wl_timer_stop(s_keepalive_global.daemon_timer);
        s_keepalive_global.daemon_timer = WL_INVALID_TIMER;
    }
    if (WL_INVALID_TIMER != s_keepalive_global.reconnect_timer) {
        wl_timer_stop(s_keepalive_global.reconnect_timer);
        s_keepalive_global.reconnect_timer = WL_INVALID_TIMER;
    }
    return 0;
}

static wl_int32 keepalive_message_callback(wl_int32 msgid, void* data, wl_uint32 datalen) {
    WLLOGD("msgid %d", msgid);
    switch (msgid) {
        case KEEPALIVE_MSG_SET_NETWORK:
            process_set_network(data, datalen);
            break;
        case KEEPALIVE_MSG_SET_RESOURCE_VERSION: {
            if (wl_null == data || sizeof(s_keepalive_global.resource_version) != datalen) {
                WLLOGW("data is null or datalen not equal msgsize");
                return -1;
            }
            memcpy(&s_keepalive_global.resource_version, data, datalen);
            break;
        }
        case KEEPALIVE_MSG_CONNECTED: {
            // 重置下次重连时间
            s_keepalive_global.reconnect_interval = RECONNECT_INTERVAL_MIN;
            s_keepalive_global.next_reconnect_timestamp =
                repo_get_server_timestamp() + s_keepalive_global.reconnect_interval;
            s_keepalive_global.next_refresh_token_timestamp = repo_get_server_timestamp() + REFRESH_TOKEN_INTERVAL;
            // 关闭重连定时器
            keepalive_stop_reconnect_timer();
            break;
        }
        case KEEPALIVE_MSG_DISCONNECTED: {
            wl_int32 state = repo_get_login_state();
            // 非法访问,重登时间延长
            if (WLSDK_LOGIN_LOGOUT == state || WLSDK_LOGIN_EXCEPTION == state || WLSDK_LOGIN_KICKOUT == state) {
                s_keepalive_global.next_reconnect_timestamp = repo_get_server_timestamp() + RECONNECT_INTERVAL_OVERTIME;
            }
            // 启动重连定时器
            keepalive_start_reconnect_timer();
            reconnect_check();
            break;
        }
        case KEEPALIVE_MSG_SEND_HEARTBEAT:
            // 两次心跳间隔不能小于心跳超时时间
            if (repo_get_server_timestamp()
                >= protocol_client_get_latest_recv_timestamp() + (PROTOCOL_CLIENT_REQUEST_TIMEOUT_30 / 1000)) {
                WLLOGD("send heartbeat");
                send_heartbeat();
            }
            break;
        case KEEPALIVE_MSG_SEND_HEARTBEAT_RESPONSE: {
            PBEncResult enc_result = weilapb_buildHeartbeatRsp();
            wl_int32    resultcode = protocol_client_send_response(&enc_result);
            if (0 > resultcode) {
                WLLOGW("protocol_client_send_response error");
                protocol_client_disconnect();
            }
            break;
        }
        case KEEPALIVE_MSG_CHECK_VERSION: {
            check_version();
            break;
        }
        case KEEPALIVE_MSG_ALARM_POWER: {
            wl_int32* percent = (wl_int32*)data;
            // 电量最低1%
            if (*percent <= 0) {
                *percent = 1;
            }
            // 告警电量低于上次告警电量或首次上报电量立刻上报
            if (s_keepalive_global.latest_alarm_power_percent > *percent
                || 0 == s_keepalive_global.latest_alarm_power_percent) {
                s_keepalive_global.next_battery_timestamp     = 0;
                s_keepalive_global.alarm_power_percent        = *percent;
                s_keepalive_global.latest_alarm_power_percent = *percent;
                battery_refresh();
            }
            break;
        }
        case KEEPALIVE_MSG_BATTERY_UPDATE: {
            wl_int32* percent = (wl_int32*)data;
            // 电量最低1%
            if (*percent <= 0) {
                *percent = 1;
            }
            // 充电
            if (*percent > 100) {
                s_keepalive_global.latest_alarm_power_percent = 0;  // 告警电量重置
                s_keepalive_global.alarm_power_percent        = 0;  // 充电不告警
                s_keepalive_global.battery_update_percent     = *percent;
                // 首次充电更新
                if (s_keepalive_global.latest_battery_update_percent <= 100) {
                    s_keepalive_global.next_battery_timestamp = 0;
                    battery_refresh();
                }
            } else {
                // 电量低于上次变更电量或首次更新,立刻更新
                if (s_keepalive_global.latest_battery_update_percent > *percent
                    || 0 == s_keepalive_global.latest_battery_update_percent) {
                    s_keepalive_global.next_battery_timestamp = 0;
                    s_keepalive_global.battery_update_percent = *percent;
                    battery_refresh();
                }
            }
            break;
        }
        case KEEPALIVE_MSG_REPORT_REBOOT: {
            wl_int32* memory = (wl_int32*)data;
            report_reboot(*memory);
            break;
        }
        case KEEPALIVE_MSG_REPORT_RESET_MODEM: {
            report_reset_modem();
            break;
        }
        case KEEPALIVE_MSG_SET_OTA_STATUS: {
            wl_int32* status              = (wl_int32*)data;
            s_keepalive_global.ota_status = *status;
            break;
        }
        case KEEPALIVE_MSG_SET_HEARTBEAT_INTERVAL: {
            wl_int32* interval                          = (wl_int32*)data;
            s_keepalive_global.heartbeat_interval_fixed = *interval;
            break;
        }
        case KEEPALIVE_MSG_SET_MEMORY_THRESHOLD: {
            wl_int32* threshold                 = (wl_int32*)data;
            s_keepalive_global.memory_threshold = *threshold;
            break;
        }
        case KEEPALIVE_MSG_SET_MEMORY_ALARM_REBOOT_HOUR: {
            wl_int32* hour                        = (wl_int32*)data;
            s_keepalive_global.memory_reboot_hour = *hour;
            break;
        }
        default:
            WLLOGW("msgid %d not support", msgid);
            break;
    }
    return 0;
}

static void reconnect_check(void) {
    // 网络已就绪
    if (WLSDK_SYSTEM_NETWORK_CONNECTED == s_keepalive_global.network_state) {
        if (repo_get_server_timestamp() >= s_keepalive_global.next_reconnect_timestamp) {
            wl_int32 state = repo_get_login_state();
            // 未登录,强行登录
            if (WLSDK_LOGIN_SYNCING != state && WLSDK_LOGIN_ONLINE != state) {
                // 断开连接
                protocol_client_disconnect();
                WLLOGD("reconnect login");
                if (wl_ext_is_network_valid()) {
                    s_keepalive_global.network_invalid_duration = 0;
                }
                wl_core_send_message(WL_MODULE_LOGIN, LOGIN_MSG_LOGIN, wl_null, 0);
                s_keepalive_global.next_reconnect_timestamp =
                    repo_get_server_timestamp() + s_keepalive_global.reconnect_interval;
                s_keepalive_global.reconnect_interval += RECONNECT_INTERVAL_STEP;
                if (RECONNECT_INTERVAL_MAX < s_keepalive_global.reconnect_interval) {
                    s_keepalive_global.reconnect_interval = RECONNECT_INTERVAL_MAX;
                }
            }
            keepalive_stop_reconnect_timer();
        }
    } else {
        keepalive_stop_reconnect_timer();
        WLLOGW("network_state is %d", s_keepalive_global.network_state);
    }
}

static void send_heartbeat(void) {
    // 连续三次心跳成功
    if (s_keepalive_global.success_times >= 3) {
        s_keepalive_global.heartbeat_interval += HEARTBEAT_INTERVAL_STEP;
        if (s_keepalive_global.heartbeat_interval > s_keepalive_global.heartbeat_interval_max) {
            s_keepalive_global.heartbeat_interval = s_keepalive_global.heartbeat_interval_max;
        }
        s_keepalive_global.success_times = 0;
    }
    // 连续三次心跳超时
    else if (s_keepalive_global.timeout_times >= 3) {
        s_keepalive_global.heartbeat_interval -= HEARTBEAT_INTERVAL_STEP;
        if (s_keepalive_global.heartbeat_interval < s_keepalive_global.heartbeat_interval_min) {
            s_keepalive_global.heartbeat_interval = s_keepalive_global.heartbeat_interval_min;
        }
        // 心跳间隔上限调整为当前间隔
        s_keepalive_global.heartbeat_interval_max = s_keepalive_global.heartbeat_interval;
        s_keepalive_global.timeout_times          = 0;
    }
    // 固定心跳
    if (s_keepalive_global.heartbeat_interval_fixed > 0) {
        if (s_keepalive_global.heartbeat_interval_max >= s_keepalive_global.heartbeat_interval_fixed) {
            s_keepalive_global.heartbeat_interval = s_keepalive_global.heartbeat_interval_fixed;
        }
    }
    {
        PBEncResult enc_result = weilapb_buildHeartbeatWithIntervalReq(s_keepalive_global.heartbeat_interval);
        wl_int32    resultcode = protocol_client_send_request(&enc_result,
                                                           wl_null,
                                                           0,
                                                           PROTOCOL_CLIENT_REQUEST_TIMEOUT_30,
                                                           keepalive_protocol_client_request_callback,
                                                           wl_null);
        if (0 > resultcode) {
            WLLOGW("protocol_client_send_request error");
            protocol_client_disconnect();
        }
        s_keepalive_global.latest_heartbeat_timestamp = repo_get_server_timestamp();
        s_keepalive_global.latest_heartbeat_recvbytes = protocol_client_get_recv_bytes();
    }
}

static void heartbeat_check(void) {
    // 在线
    if (WLSDK_LOGIN_ONLINE == repo_get_login_state() || WLSDK_LOGIN_SYNCING == repo_get_login_state()) {
        // 心跳周期间没有通讯,发送心跳
        if (repo_get_server_timestamp()
            >= protocol_client_get_latest_recv_timestamp() + s_keepalive_global.heartbeat_interval) {
            wl_core_send_message(WL_MODULE_KEEPALIVE, KEEPALIVE_MSG_SEND_HEARTBEAT, wl_null, 0);
        }
    }
}

static void token_refresh(void) {
    // 在线
    if (WLSDK_LOGIN_ONLINE == repo_get_login_state() || WLSDK_LOGIN_SYNCING == repo_get_login_state()) {
        wl_time current_time = repo_get_server_timestamp();
        if (s_keepalive_global.next_refresh_token_timestamp > current_time + REFRESH_TOKEN_INTERVAL) {
            s_keepalive_global.next_refresh_token_timestamp = current_time + REFRESH_TOKEN_INTERVAL;
        }
        // 到达刷新
        if (current_time > s_keepalive_global.next_refresh_token_timestamp) {
            wl_core_send_message(WL_MODULE_LOGIN, LOGIN_MSG_REFRESH_ACCESS_TOKEN, wl_null, 0);
            s_keepalive_global.next_refresh_token_timestamp = current_time + REFRESH_TOKEN_INTERVAL;
        }
    }
}

static void build_http_param(WLcJSON* obj) {
    repo_hardware hardware;
    repo_get_hardware(&hardware);

    repo_build_http_sign(obj);
    WLcJSON_AddStringToObject(obj, "sdk_version", WLSDK_VERSION);
    WLcJSON_AddNumberToObject(obj, "version_number", hardware.version_number);
    if (strlen(s_keepalive_global.resource_version.version) > 0) {
        WLcJSON_AddNumberToObject(obj, "resource_version_number", s_keepalive_global.resource_version.version_number);
    }
    WLcJSON_AddStringToObject(obj, "firmware", hardware.firmware);
}

static void check_version(void) {
    if (0 != s_keepalive_global.ota_status) {
        return;
    }
    // 网络连接成功
    if (WLSDK_SYSTEM_NETWORK_CONNECTED == s_keepalive_global.network_state) {
        wl_char     server_url[MAX_URL_LEN];
        WLcJSON*    body_obj = wl_null;
        wl_char*    content  = wl_null;
        repo_server web_server;
        repo_get_web_server(&web_server);
        s_keepalive_global.check_version_left = VERSION_CHECK_INTERVAL;
        SERVER_URL(server_url, web_server.host, web_server.port, "check-version");
        body_obj = WLcJSON_CreateObject();
        if (wl_null == body_obj) {
            WLLOGW("create obj fail");
            return;
        }
        build_http_param(body_obj);
        content = WLcJSON_PrintUnformatted(body_obj);
        WLcJSON_Delete(body_obj);
        if (content) {
            wl_http_client_post(server_url,
                             HTTP_CLIENT_REQUEST_TIMEOUT_30,
                             content,
                             strlen(content),
                             HTTP_CLIENT_CONTENT_JSON,
                             keepalive_wl_http_client_callback,
                             (void*)KEEPALIVE_HTTP_REQUEST_CHECK_VERSION);
            WLcJSON_free(content);
            return;
        }
    } else {
        keepalive_sdk_message_callback(
            WLSDK_MSG_SYSTEM_NEW_VERSION, WLSDK_RESULT_LOCAL_NETWORK_DISCONNECTED, wl_null, 0);
    }
}

static void alarm_power(wl_int32 percent) {
    // 网络连接成功
    if (WLSDK_SYSTEM_NETWORK_CONNECTED == s_keepalive_global.network_state) {
        wl_char     server_url[MAX_URL_LEN];
        WLcJSON*    body_obj = wl_null;
        wl_char*    content  = wl_null;
        repo_server web_server;
        wl_char     token[MAX_TOKEN_LEN + 1];

        repo_get_web_server(&web_server);
        repo_get_token(token);
        sprintf(server_url,
                "http://%s:%d/v1/users/device/report-power-alarm?access-token=%s",
                web_server.host,
                web_server.port,
                token);
        body_obj = WLcJSON_CreateObject();
        if (wl_null == body_obj) {
            WLLOGW("create obj fail");
            return;
        }

        repo_build_http_sign(body_obj);
        WLcJSON_AddNumberToObject(body_obj, "percent", percent);

        content = WLcJSON_PrintUnformatted(body_obj);
        WLcJSON_Delete(body_obj);
        if (content) {
            wl_http_client_post(server_url,
                             HTTP_CLIENT_REQUEST_TIMEOUT_30,
                             content,
                             strlen(content),
                             HTTP_CLIENT_CONTENT_JSON,
                             keepalive_wl_http_client_callback,
                             (void*)KEEPALIVE_HTTP_REQUEST_ALARM_POWER);
            WLcJSON_free(content);
            return;
        }
    } else {
        keepalive_sdk_message_callback(
            WLSDK_MSG_SYSTEM_ALARM_POWER, WLSDK_RESULT_LOCAL_NETWORK_DISCONNECTED, wl_null, 0);
    }
}

static void report_reboot(wl_int32 memory) {
    // 网络连接成功
    if (WLSDK_SYSTEM_NETWORK_CONNECTED == s_keepalive_global.network_state) {
        wl_char       server_url[MAX_URL_LEN];
        WLcJSON*      body_obj = wl_null;
        wl_char*      content  = wl_null;
        repo_server   web_server;
        repo_hardware hardware;
        repo_get_hardware(&hardware);

        repo_get_web_server(&web_server);
        SERVER_URL(server_url, web_server.host, web_server.port, "report-reboot");
        body_obj = WLcJSON_CreateObject();
        if (wl_null == body_obj) {
            WLLOGW("create obj fail");
            return;
        }
        build_http_param(body_obj);
        WLcJSON_AddNumberToObject(body_obj, "last_version_num", repo_get_system_last_version_num());
        WLcJSON_AddNumberToObject(body_obj, "last_daemon_duration", repo_get_system_last_daemon_duration());
        WLcJSON_AddNumberToObject(body_obj, "last_memory", repo_get_system_last_memory());
        WLcJSON_AddNumberToObject(body_obj, "last_sdk_memory", repo_get_system_last_sdk_memory());
        WLcJSON_AddNumberToObject(body_obj, "last_memory_reboot", repo_get_system_last_memory_reboot());
        WLcJSON_AddNumberToObject(body_obj, "memory", memory);
        content = WLcJSON_PrintUnformatted(body_obj);
        WLcJSON_Delete(body_obj);
        if (content) {
            wl_http_client_post(server_url,
                             HTTP_CLIENT_REQUEST_TIMEOUT_30,
                             content,
                             strlen(content),
                             HTTP_CLIENT_CONTENT_JSON,
                             keepalive_wl_http_client_callback,
                             (void*)KEEPALIVE_HTTP_REQUEST_REPORT_REBOOT);
            WLcJSON_free(content);
            return;
        }
    }
}

static void report_reset_modem(void) {
    // 网络连接成功
    if (WLSDK_SYSTEM_NETWORK_CONNECTED == s_keepalive_global.network_state) {
        wl_char     server_url[MAX_URL_LEN];
        WLcJSON*    body_obj = wl_null;
        wl_char*    content  = wl_null;
        repo_server web_server;

        repo_get_web_server(&web_server);
        SERVER_URL(server_url, web_server.host, web_server.port, "report-reset-modem");
        body_obj = WLcJSON_CreateObject();
        if (wl_null == body_obj) {
            WLLOGW("create obj fail");
            return;
        }
        build_http_param(body_obj);
        content = WLcJSON_PrintUnformatted(body_obj);
        WLcJSON_Delete(body_obj);
        if (content) {
            wl_http_client_post(server_url,
                             HTTP_CLIENT_REQUEST_TIMEOUT_30,
                             content,
                             strlen(content),
                             HTTP_CLIENT_CONTENT_JSON,
                             keepalive_wl_http_client_callback,
                             (void*)KEEPALIVE_HTTP_REQUEST_REPORT_RESET_MODEM);
            WLcJSON_free(content);
            return;
        }
    }
}

static void battery_update(wl_int32 percent) {
    PB_CHAR battery[MAX_ID_LEN];
    sprintf(battery, "%d", percent);
    {
        PBEncResult enc_result = weilapb_buildSetExtensionConfigReq("battery", battery);
        protocol_client_send_request(&enc_result, wl_null, 0, PROTOCOL_CLIENT_REQUEST_TIMEOUT_30, wl_null, wl_null);
    }
}

static void session_check(void) {
    if (WLSDK_LOGIN_ONLINE == repo_get_login_state()) {
        wl_core_send_message(WL_MODULE_BURST, BURST_MSG_CHECK_SESSION, wl_null, 0);
    }
}

static void version_check(void) {
    if (WLSDK_SYSTEM_NETWORK_CONNECTED == s_keepalive_global.network_state) {
        if (s_keepalive_global.check_version_left <= 0) {
            check_version();
        }
    }
}

static void battery_refresh(void) {
    // 在线
    if (WLSDK_LOGIN_ONLINE == repo_get_login_state()) {
        wl_time current_time = repo_get_server_timestamp();
        if (current_time > s_keepalive_global.next_battery_timestamp) {
            if (s_keepalive_global.battery_update_percent > 0) {
                s_keepalive_global.latest_battery_update_percent = s_keepalive_global.battery_update_percent;
                battery_update(s_keepalive_global.battery_update_percent);
                s_keepalive_global.battery_update_percent = 0;
                s_keepalive_global.next_battery_timestamp = current_time + BATTERY_REPORT_INTERVAL;
            }
            if (s_keepalive_global.alarm_power_percent > 0) {
                alarm_power(s_keepalive_global.alarm_power_percent);
                s_keepalive_global.alarm_power_percent    = 0;
                s_keepalive_global.next_battery_timestamp = current_time + BATTERY_REPORT_INTERVAL;
            }
        }
    }
}

static void memory_check(void) {
    wl_uint32 size = wl_mem_sys_size();
    if (size < s_keepalive_global.memory_threshold) {
        WLLOGD("system mem %d threshold %d times %d reboot hour %d",
               wl_mem_sys_size(),
               s_keepalive_global.memory_threshold,
               s_keepalive_global.memory_threshold_times,
               s_keepalive_global.memory_reboot_hour);
        s_keepalive_global.memory_threshold_times++;
        if (s_keepalive_global.memory_threshold_times > SYSTEM_MEMORY_THRESHOLD_TIMES) {
            // 每天凌晨重启
            wl_tm localtime;
            memset(&localtime, 0, sizeof(wl_tm));
            if (0 == wl_localtime(&localtime)) {
                WLLOGD("localtime hour %d min %d", localtime.tm_hour, localtime.tm_min);
                // 重启(小时与分钟都比较,防止重复重启)
                if (s_keepalive_global.memory_reboot_hour == localtime.tm_hour
                    && s_keepalive_global.memory_reboot_min == localtime.tm_min) {
                    s_keepalive_global.memory_is_reboot = wl_true;
                }
            }
            // 无法获取本地时间等内存告警
            else {
                WLLOGE("localtime invalid");
                // 内存告警,立即重启
                if (size < (WL_MEMORY_SYSTEM_ALARM_SIZE)) {
                    s_keepalive_global.memory_is_reboot = wl_true;
                }
            }
            if (s_keepalive_global.memory_is_reboot) {
                repo_set_system_info(
                    s_keepalive_global.daemon_duration, 1, wl_mem_fresh_size() + wl_mem_free_size(), wl_mem_sys_size());
                wl_msleep(100);
                keepalive_sdk_message_callback(
                    WLSDK_MSG_SYSTEM_EXCEPTION, WLSDK_RESULT_LOCAL_OUT_OF_MEMORY, wl_null, 0);
            }
            return;
        }
    } else {
        s_keepalive_global.memory_threshold_times = 0;
    }
}

static void keepalive_daemon_timer_callback(WL_TIMER timer, void* context) {
    if (timer != s_keepalive_global.daemon_timer) {
        WLLOGW("timer %p invalid", timer);
        wl_timer_stop(timer);
    }
    // 校准服务器时间
    repo_adjust_server_timestamp(DAEMON_TIMER_INTERVAL);
    s_keepalive_global.daemon_duration += DAEMON_TIMER_INTERVAL;
    s_keepalive_global.check_version_left -= DAEMON_TIMER_INTERVAL;

    WLLOGD("userid %d daemon %d net state %d invalid %d system invalid %d version check left %d login state %d bind "
           "state %d ",
           repo_get_userid(),
           s_keepalive_global.daemon_duration,
           s_keepalive_global.network_state,
           s_keepalive_global.network_invalid_duration,
           s_keepalive_global.system_invalid_duration,
           s_keepalive_global.check_version_left,
           repo_get_login_state(),
           repo_get_extension_state());
    WLLOGD("mem used %d size %d fresh %d size %d free %d size %d sys size %d",
           wl_mem_num_used(),
           wl_mem_used_size(),
           wl_mem_num_fresh(),
           wl_mem_fresh_size(),         
           wl_mem_num_free(),
           wl_mem_free_size(),
           wl_mem_sys_size());
    WLLOGD("server timestamp %d refresh %d reconnect %d",
           repo_get_server_timestamp(),
           s_keepalive_global.next_refresh_token_timestamp,
           s_keepalive_global.next_reconnect_timestamp);

    // 网络已连接,检查网络是否有效
    // 网络有效检查
    if (wl_ext_is_network_valid()) {
        // socket轮询,防止数据接收阻塞
        // wl_socket_poll();
        s_keepalive_global.network_invalid_duration = 0;
        s_keepalive_global.system_invalid_duration  = 0;
    } else {
        s_keepalive_global.network_invalid_duration += DAEMON_TIMER_INTERVAL;
        s_keepalive_global.system_invalid_duration += DAEMON_TIMER_INTERVAL;
        // 网络无效
        if (s_keepalive_global.network_invalid_duration >= NETWORK_INVALID_MAX) {
            WLLOGW("network invalid");
            s_keepalive_global.network_invalid_duration = 0;
            keepalive_sdk_message_callback(
                WLSDK_MSG_SYSTEM_NETWORK_EXCEPTION, WLSDK_RESULT_LOCAL_CONNECT_FAIL, wl_null, 0);
        }
        // 系统无效
        if (s_keepalive_global.system_invalid_duration >= SYSTEM_INVALID_MAX) {
            WLLOGW("system invalid");
            s_keepalive_global.system_invalid_duration = 0;
            keepalive_sdk_message_callback(WLSDK_MSG_SYSTEM_EXCEPTION, WLSDK_RESULT_LOCAL_CONNECT_FAIL, wl_null, 0);
        }
    }
    s_keepalive_global.next_system_save_left -= DAEMON_TIMER_INTERVAL;
    if (s_keepalive_global.next_system_save_left <= 0) {
        s_keepalive_global.next_system_save_left = SYSTEM_SAVE_INTERVAL;
        repo_set_system_info(
            s_keepalive_global.daemon_duration, 0, wl_mem_fresh_size() + wl_mem_free_size(), wl_mem_sys_size());
    }

    // 内存异常检查
    memory_check();

    // 重连检查
    reconnect_check();

    // 心跳检查
    heartbeat_check();

    // token刷新
    token_refresh();

    // session检查
    session_check();

    // 版本检查
    version_check();

    // 电量刷新
    battery_refresh();
}

static void keepalive_reconnect_timer_callback(WL_TIMER timer, void* context) {
    if (timer != s_keepalive_global.reconnect_timer) {
        WLLOGW("timer %p invalid", timer);
        wl_timer_stop(timer);
    }
    // 重连检查
    reconnect_check();
}

static wl_int32 keepalive_protocol_client_request_callback(PBWeilaMsg* message,
                                                           void*       content,
                                                           wl_uint32   contentlen,
                                                           void*       context) {
    WLLOGD("message %d resultCode %d", message->message, message->resultCode);
    switch (message->message) {
        case WL_LOGIN_HEARTBEAT_MSG:
            // 心跳异常断开链接
            if (WLSDK_RESULT_SUCCESS != message->resultCode) {
                // 请求超时
                if (WLSDK_RESULT_LOCAL_REQUEST_TIMEOUT == message->resultCode) {
                    // 心跳期间有通讯,网络阻塞判定,重新发送一次心跳
                    if (protocol_client_get_recv_bytes() > s_keepalive_global.latest_heartbeat_recvbytes) {
                        wl_core_send_message(WL_MODULE_KEEPALIVE, KEEPALIVE_MSG_SEND_HEARTBEAT, wl_null, 0);
                    }
                    // 心跳期间无通讯,超时判定
                    else {
                        wl_core_send_message(WL_MODULE_LOGIN, LOGIN_MSG_CLOSE_PROTOCOL_CLIENT, wl_null, 0);
                        s_keepalive_global.success_times = 0;  // 心跳成功重置计数
                        s_keepalive_global.timeout_times++;    // 心跳超时计数
                    }
                }
                // 其他异常,登录无效
                else {
                    wl_core_send_message(WL_MODULE_LOGIN, LOGIN_MSG_CLOSE_PROTOCOL_CLIENT, wl_null, 0);
                }
            } else {
                PB_UINT32* serverTime = (PB_UINT32*)message->pContent;
                if (serverTime) {
                    repo_set_server_timestamp(*serverTime);
                }
                // 请求成功,动态心跳调整
                // 心跳期间无通讯,调整心跳
                if (protocol_client_get_recv_bytes() == s_keepalive_global.latest_heartbeat_recvbytes) {
                    s_keepalive_global.success_times++;    // 心跳成功计数
                    s_keepalive_global.timeout_times = 0;  // 心跳超时重置计数
                }
            }
            break;
        default:
            break;
    }
    return 0;
}

static void process_set_network(void* data, wl_int32 datalen) {
    keepalive_network msgdata;
    if (wl_null == data || sizeof(msgdata) != datalen) {
        WLLOGW("data is null or datalen not equal msgsize");
        return;
    }
    memcpy(&msgdata, data, datalen);
    // wl_socket_set_network(msgdata.simid, msgdata.pdpid, msgdata.state);

    // 网络切换
    if (s_keepalive_global.network_state != msgdata.state) {
        s_keepalive_global.network_state = msgdata.state;

        // 断开连接
        protocol_client_disconnect();

        // 网络连接成功,重新登录
        if (WLSDK_SYSTEM_NETWORK_CONNECTED == msgdata.state) {
            // 重置定时器
            keepalive_start_reconnect_timer();

            // 网络变更,重置心跳间隔
            s_keepalive_global.heartbeat_interval     = HEARTBEAT_INTERVAL_MIN;
            s_keepalive_global.heartbeat_interval_max = HEARTBEAT_INTERVAL_MAX;
            s_keepalive_global.heartbeat_interval_min = HEARTBEAT_INTERVAL_MIN;
            s_keepalive_global.success_times          = 0;
            s_keepalive_global.timeout_times          = 0;
            // 重置下次重连时间
            s_keepalive_global.reconnect_interval = RECONNECT_INTERVAL_MIN;
            // 网络重置,重连等待时间最长为RECONNECT_INTERVAL_MIN
            if (s_keepalive_global.next_reconnect_timestamp
                > repo_get_server_timestamp() + s_keepalive_global.reconnect_interval) {
                s_keepalive_global.next_reconnect_timestamp =
                    repo_get_server_timestamp() + s_keepalive_global.reconnect_interval;
            }
            reconnect_check();
        }
    }
}

#ifdef __cplusplus
}
#endif
